/**
 * $Id: FreemarkerViewProcessor.java 2 2011-09-03 10:16:43Z adriftrock@gmail.com $
 */

package com.swehr.jersey;

import com.sun.jersey.api.view.Viewable;
import com.sun.jersey.spi.resource.Singleton;
import com.sun.jersey.spi.template.ViewProcessor;
import freemarker.cache.WebappTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.log4j.Logger;

import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

/**
* @author Rock Huang
* @version 0.1
*/
@Provider
@Singleton
public class FreemarkerViewProcessor implements ViewProcessor<Template> {

    private static final Logger log = Logger.getLogger(FreemarkerViewProcessor.class);
    private static final String DEFAULT_EXTENSION = ".ftl";
    private static final String RT_PATH = "/views";
    private static Configuration cfg = new Configuration();

    private String rootPath;
    private ServletContext servletContext;

    @Context
    public void setServletContext(ServletContext context) {
        this.servletContext = context;
        rootPath = context.getInitParameter("view.path");
        if (rootPath == null || rootPath.trim().length() == 0) {
            rootPath = RT_PATH;
        }
        rootPath = rootPath.replaceAll("/$", "");
        cfg.setTemplateLoader(new WebappTemplateLoader(context, rootPath));
        InputStream prop = context.getResourceAsStream("freemarker.properties");
        if (prop != null) {
            try {
                cfg.setSettings(prop);
                return;
            } catch (Exception e) {
                log.warn("Failed to load freemarker.properties, will use " +
                        "default settings instead; error: " + e.getMessage());
            }
        }
        defaultFreemarkerConfig();
    }

    private void defaultFreemarkerConfig() {
        cfg.setNumberFormat("0"); // don't always put a ',' in numbers
        cfg.setLocalizedLookup(false); // don't look for list.en.ftl when list.ftl requested
        cfg.setTemplateUpdateDelay(0); // don't cache
    }

    /**
     * accept both '/path/to/template' and '/path/to/template.ftl'
     *
     * @param path A path of template.
     * @return Object
     */
    public Template resolve(String path) {
        String filePath = path.endsWith(DEFAULT_EXTENSION) ? path : path + DEFAULT_EXTENSION;
        try {
            final String fullPath = rootPath + filePath;
            final boolean templateFound = servletContext.getResource(fullPath) != null;
            if (!templateFound)
                log.warn("Template not found [Given path: " + path + "] " +
                        "[Servlet context path: " + fullPath + "]");
            return templateFound ? cfg.getTemplate(path) : null;
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("Probably cannot found freemarker resource (" + filePath +
                    ") from the servlet context: " + e.getMessage());
            return null;
        }
    }

    public void writeTo(Template template, Viewable viewable, OutputStream out)
            throws IOException {
        try {
            template.process(viewable.getModel(), new OutputStreamWriter(out));
        } catch (final TemplateException e) {
            throw new IOException(e);
        }
    }

}
